home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 4 / Info_Mac IV CD-ROM (Pacific HiTech Inc.)(August 1994).iso / Development / Source / PopupCDEF 1.0b3 / PopupCDEF-demo.c < prev    next >
Text File  |  1994-03-16  |  15KB  |  525 lines

  1. /* See the file Distribution for distribution terms.
  2.     (c) Copyright 1994 Ari Halberstadt */
  3.  
  4. /* A simple program to test my Popup CDEF. A dialog containing various
  5.     types of popup controls is displayed. You can make selections from the
  6.     menus, and notice how the current item is marked with a check mark.
  7.     Click the "Quit" button to quit the program.
  8.     
  9.     A type-in popup menu is also demonstrated, and a functions that show
  10.     how type-in popup menus can be supported are provided. To make it
  11.     easier to find the relavent code, sections of code that are used
  12.     for the type-in popup menu are bracketed with "TYPEIN_BEGIN" and
  13.     "TYPEIN_END".
  14.     
  15.     94/03/14 aih - created by hacking Thread Library test application */
  16.  
  17. #include <GestaltEqu.h>
  18. #include <Traps.h>
  19. #include "PopupLib.h"
  20.  
  21. /*----------------------------------------------------------------------------*/
  22. /* global definitions and declarations */
  23. /*----------------------------------------------------------------------------*/
  24.  
  25. /* It is much easier to debug an application than it is to debug
  26.     a code resource. So that the CDEF can be debugged from within
  27.     an application, we can attach the CDEF compiled as part of this
  28.     application to a popup menu control, replacing the 'CDEF'
  29.     resource for the control. If you define CDEF_ATTACH as 1 then
  30.     the CDEF will be "attached", so that it can be debugged within
  31.     this application. */
  32. #ifndef CDEF_ATTACH
  33.     #define CDEF_ATTACH (0)
  34. #endif /* CDEF_ATTACH */
  35.  
  36. /* dialog items */
  37. enum {
  38.     rDialog = 128,
  39.     iQuit = 1,
  40.     iFontPopup,
  41.     iSizeTitle,
  42.     iSizeText,
  43.     iSizePopup,
  44.     iStylePopup,
  45.     iAlignPopup,
  46.     iLongTitlePopup,
  47.     iIconsPopup,
  48.     iRightAlignedPopup,
  49.     iWindowFontPopup,
  50.     iDisabledItemPopup,
  51.     iDisabledPopup,
  52.     iNoTitlePopup,
  53.     iLast
  54. };
  55.  
  56. /*----------------------------------------------------------------------------*/
  57. /* assertions */
  58. /*----------------------------------------------------------------------------*/
  59.  
  60. #ifndef NDEBUG
  61.     #define myassert(x) ((void) ((x) || assertfailed()))
  62. #else
  63.     #define myassert(x) ((void) 0)
  64. #endif
  65.  
  66. #define require(x)    myassert(x)
  67. #define check(x)        myassert(x)
  68. #define ensure(x)        myassert(x)
  69.  
  70. static int assertfailed(void)
  71. {
  72.     DebugStr((StringPtr) "\p An assertion failed.");
  73.     return(0);
  74. }
  75.  
  76. /*----------------------------------------------------------------------------*/
  77. /* standard Macintosh initializations */
  78. /*----------------------------------------------------------------------------*/
  79.  
  80. /* initialize application heap */
  81. static void HeapInit(long stack, short masters)
  82. {
  83.     SetApplLimit(GetApplLimit() - stack);
  84.     MaxApplZone();
  85.     while (masters-- > 0)
  86.         MoreMasters();
  87. }
  88.  
  89. /* initialize managers */
  90. static void ManagersInit(void)
  91. {
  92.     EventRecord event;
  93.     short i;
  94.     
  95.     /* standard initializations */
  96.     InitGraf((Ptr) &thePort);
  97.     InitFonts();
  98.     InitWindows();
  99.     InitMenus();
  100.     TEInit();
  101.     InitDialogs(NULL);
  102.     FlushEvents(everyEvent, 0);
  103.     InitCursor();
  104.     
  105.     /* so first window will be frontmost */
  106.     for (i = 0; i < 4; i++)
  107.         EventAvail(everyEvent, &event);
  108. }
  109.  
  110. /*----------------------------------------------------------------------------*/
  111. /* event utilities */
  112. /*----------------------------------------------------------------------------*/
  113.  
  114. /* Functions for determining whether a trap is available. Based on
  115.     functions given in IM VI. */
  116.  
  117. /* return number of toolbox traps */
  118. static short TrapNumToolbox(void)
  119. {
  120.     short result = 0;
  121.     
  122.     if (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xAA6E, ToolTrap))
  123.         result = 0x0200;
  124.     else
  125.         result = 0x0400;
  126.     return(result);
  127. }
  128.  
  129. /* return the type of the trap */
  130. static TrapType TrapTypeGet(short trap)
  131. {
  132.     return((trap & 0x0800) > 0 ? ToolTrap : OSTrap);
  133. }
  134.  
  135. /* true if the trap is available  */
  136. static Boolean TrapAvailable(short trap)
  137. {
  138.     TrapType type;
  139.     
  140.     type = TrapTypeGet(trap);
  141.     if (type == ToolTrap) {
  142.         trap &= 0x07FF;
  143.         if (trap >= TrapNumToolbox())
  144.             trap = _Unimplemented;
  145.     }
  146.     return(NGetTrapAddress(trap, type) != NGetTrapAddress(_Unimplemented, ToolTrap));
  147. }
  148.  
  149. /* true if the WaitNextEvent trap is available */
  150. static Boolean MacHasWNE(void)
  151. {
  152.     static Boolean initialized;
  153.     static Boolean wne;
  154.     
  155.     if (! initialized) {
  156.         /* do only once for efficiency */
  157.         wne = TrapAvailable(_WaitNextEvent);
  158.         initialized = true;
  159.     }
  160.     return(wne);
  161. }
  162.  
  163. /* Call GetNextEvent or WaitNextEvent, depending on which one is available.
  164.     The parameters to this function are identical to those to WaitNextEvent.
  165.     If GetNextEvent is called the extra parameters are ignored. */
  166. static Boolean EventGet(short mask, EventRecord *event,
  167.     unsigned long sleep, RgnHandle cursor)
  168. {
  169.     Boolean result = false;
  170.  
  171.     if (MacHasWNE())
  172.         result = WaitNextEvent(mask, event, sleep, cursor);
  173.     else {
  174.         SystemTask();
  175.         result = GetNextEvent(mask, event);
  176.     }
  177.     if (! result) {
  178.         /* make sure it's a null event, even if the system thinks otherwise, e.g.,
  179.             some desk accessory events (see comment in TransSkell event loop) */
  180.         event->what = nullEvent;
  181.     }
  182.     return(result);
  183. }
  184.  
  185. /*----------------------------------------------------------------------------*/
  186. /* dialog utilities */
  187. /*----------------------------------------------------------------------------*/
  188.  
  189. /* get the text of the dialog item */
  190. static void GetDText(DialogPtr dlg, short item, Str255 str)
  191. {
  192.     short type;
  193.     Handle hitem;
  194.     Rect box;
  195.  
  196.     GetDItem(dlg, item, &type, &hitem, &box);
  197.     GetIText(hitem, str);
  198. }
  199.  
  200. /* set the text of the dialog item */
  201. static void SetDText(DialogPtr dlg, short item, const Str255 str)
  202. {
  203.     short type;
  204.     Handle hitem;
  205.     Rect box;
  206.  
  207.     GetDItem(dlg, item, &type, &hitem, &box);
  208.     SetIText(hitem, str);
  209. }
  210.  
  211. /* return the numeric value of the dialog item */
  212. static long GetDNum(DialogPtr dlg, short item)
  213. {
  214.     long num;
  215.     Str255 str;
  216.     
  217.     GetDText(dlg, item, str);
  218.     StringToNum(str, &num);
  219.     return(num);
  220. }
  221.  
  222. /* set the text of the dialog item to the number */
  223. static void SetDNum(DialogPtr dlg, short item, long num)
  224. {
  225.     Str255 str;
  226.  
  227.     NumToString(num, str);
  228.     SetDText(dlg, item, str);
  229. }
  230.  
  231. /* return the control handle for the item */
  232. static ControlHandle GetDControl(DialogPtr dlg, short item)
  233. {
  234.     short type;
  235.     Handle hitem;
  236.     Rect box;
  237.  
  238.     GetDItem(dlg, item, &type, &hitem, &box);
  239.     return((ControlHandle) hitem);
  240. }
  241.  
  242. /* return a handle to the popup control's menu */
  243. static MenuHandle GetCtlMenu(ControlHandle ctl)
  244. {
  245.     return((**(PopupPrivateHandle) (**ctl).contrlData).mHandle);
  246. }
  247.  
  248. /*----------------------------------------------------------------------------*/
  249. /* menu utilities */
  250. /*----------------------------------------------------------------------------*/
  251.  
  252. /*    Given a font family id and true in 'outlined', OutlineFontSizes will outline
  253.     all items in a size menu that actually exist in that font. If 'outlined' is
  254.     false, all items will be set to plain text, which is useful if you don't
  255.     have any font information. */
  256. static void OutlineFontSizes(MenuHandle menu, short family, Boolean outlined)
  257. {
  258.     short        nitems;    /* number of items in menu */
  259.     short        item;        /* current item number */
  260.     long        size;        /* size of current menu item */
  261.     Str255    name;        /* name of item */
  262.     Boolean    found;    /* flag that we've already found the menu item */
  263.  
  264.     found = false;
  265.     nitems = CountMItems(menu);
  266.     for (item = 1; item <= nitems; item++) {
  267.         GetItem(menu, item, name);
  268.         StringToNum(name, &size);
  269.         if (outlined && RealFont(family, size))
  270.             SetItemStyle(menu, item, outline);
  271.         else
  272.             SetItemStyle(menu, item, 0);
  273.     }
  274. }
  275.  
  276. /* TYPEIN_BEGIN */
  277. /* return item number with given title, or 0 if not found */
  278. static short FindMenuItem(MenuHandle menu, const Str255 ptitle)
  279. {
  280.     short item;        /* index to menu items */
  281.     short nitems;    /* number of items in menu */
  282.     Str255 name;    /* name of current item */
  283.     
  284.     nitems = CountMItems(menu);
  285.     for (item = 1; item <= nitems; item++) {
  286.         GetItem(menu, item, name);
  287.         if (EqualString(ptitle, name, false, true))
  288.             break;
  289.     }
  290.     return(item <= nitems ? item : 0);
  291. }
  292. /* TYPEIN_END */
  293.  
  294. /* TYPEIN_BEGIN */
  295. /* AdjustTypeInPopupMenu adjusts a type-in popup menu. The 'dlg' parameter
  296.     is a pointer to the dialog containing the type-in popup meun. The
  297.     'popupItem' parameter is the item number of a type-in popup control. The
  298.     'textItem' parameter is the item number of the type-in editable text field.
  299.     The 'insertedCustomValue' parameter should initially be false; subsequently,
  300.     it must contain the value returned by the previous call to
  301.     AdjustTypeInPopupMenu.
  302.     
  303.     You should call AdjustTypeInPopupMenu whenever there's a click in the
  304.     popup menu control, but before TrackControl (or DialogSelect) is called
  305.     to handle the click. If the user entered a value into the text field that
  306.     is not in the popup menu, then the user's entry is inserted as the
  307.     first item in the popup menu and a dashed line is inserted to separate
  308.     the user's entry from the predefined values in the menu. When the user
  309.     chooses a menu item or enters a value into the text field that is one of
  310.     the predefined values in the menu, then the items inserted into the menu
  311.     are deleted. The 'insertedCustomValue' flag is used to determine if a
  312.     user's entry was inserted into the menu. */
  313. static Boolean AdjustTypeInPopupMenu(DialogPtr dlg,
  314.     short popupItem, short textItem,
  315.     Boolean insertedCustomValue)
  316. {
  317.     ControlHandle ctl;    /* handle to size popup control */
  318.     MenuHandle menu;        /* handle to popup menu's handle */
  319.     short item;                /* index to item in menu */
  320.     short nitems;            /* number of items in size menu */
  321.     Str255 text;            /* string in text item */
  322.     
  323.     /* adjust user's choice */
  324.     GetDText(dlg, textItem, text);
  325.     ctl = GetDControl(dlg, popupItem);
  326.     menu = GetCtlMenu(ctl);
  327.     item = FindMenuItem(menu, text);
  328.     if (item == 0) {
  329.  
  330.         /* user entered a size not found in the menu, so add the user's
  331.             choice as the first item in the menu (IM-VI, p2-37) */
  332.         if (! insertedCustomValue) {
  333.             InsMenuItem(menu, (StringPtr) "\p(-", 0);
  334.             InsMenuItem(menu, text, 0);
  335.             for (nitems = CountMItems(menu); nitems > 0; nitems--)
  336.                 SetItemMark(menu, nitems, noMark);
  337.             SetItemMark(menu, 1, checkMark);
  338.             SetCtlMax(ctl, GetCtlMax(ctl) + 2);
  339.             insertedCustomValue = true;
  340.         }
  341.         SetItem(menu, 1, text);
  342.         SetCtlValue(ctl, 1);
  343.     }
  344.     else if (item > 2 && insertedCustomValue) {
  345.  
  346.         /* remove user's choice, since selected item is in menu */
  347.         insertedCustomValue = false;
  348.         SetCtlValue(ctl, 1);
  349.         DelMenuItem(menu, 1);
  350.         DelMenuItem(menu, 1);
  351.         SetCtlMax(ctl, GetCtlMax(ctl) - 2);
  352.         SetCtlValue(ctl, item - 2);
  353.     }
  354.     else
  355.         SetCtlValue(ctl, item);
  356.  
  357.     return(insertedCustomValue);
  358. }
  359. /* TYPEIN_END */
  360.  
  361. /*----------------------------------------------------------------------------*/
  362. /* The event loop. */
  363. /*----------------------------------------------------------------------------*/
  364.  
  365. /* abort on error */
  366. static void fatal(Boolean exit, const StringPtr msg)
  367. {
  368.     if (exit) {
  369.         DebugStr(msg);
  370.         ExitToShell();
  371.     }
  372. }
  373.  
  374. /* create the dialog and run the program */
  375. static void Run(void)
  376. {
  377.     Boolean sizeHasCustomValue;/* true if size menu has a custom value */
  378.     EventRecord event;            /* event record for getting next event */
  379.     WindowPtr window;                /* for handling window events */
  380.     DialogPtr dlg;                    /* popup demo dialog */
  381.     Boolean quit;                    /* true if time to quit application */
  382.     Str255 str;                        /* utility string */
  383.     short font;                        /* for getting font */
  384.     short i;                            /* index to popup menu controls */
  385.     const short popups[] = {    /* all of the popup menu controls in the dialog */
  386.         iFontPopup,
  387.         iSizePopup,
  388.         iStylePopup,
  389.         iAlignPopup,
  390.         iLongTitlePopup,
  391.         iIconsPopup,
  392.         iRightAlignedPopup,
  393.         iWindowFontPopup,
  394.         iDisabledItemPopup,
  395.         iDisabledPopup,
  396.         iNoTitlePopup,
  397.         iLast,
  398.     };
  399.  
  400.     /* create the demo dialog */
  401.     dlg = GetNewDialog(rDialog, NULL, (WindowPtr) -1);
  402.     fatal(dlg == NULL, "\p nil dialog pointer");
  403.     SelIText(dlg, iSizeText, 0, 32767);
  404.     
  405.     #if CDEF_ATTACH
  406.         /* It is much easier to debug an application than it is to debug
  407.             a code resource. So that the CDEF can be debugged from within
  408.             an application, we attach the CDEF compiled as part of this
  409.             application to a popup menu control, replacing the 'CDEF'
  410.             resource for the control. */
  411.         for (i = 0; popups[i] != iLast; i++)
  412.             PopupCDEFAttach(GetDControl(dlg, popups[i]));
  413.     #endif /* CDEF_ATTACH */
  414.     
  415.     /* disable a popup menu control so we can see what a disabled popup
  416.         menu control looks like */
  417.     HiliteControl(GetDControl(dlg, iDisabledPopup), 255);
  418.     
  419.     /* display the dialog and handle events */
  420.     ShowWindow(dlg);
  421.     sizeHasCustomValue = false;
  422.     quit = false;
  423.     while (! quit) {
  424.     
  425.         /* Set the dialog's font and font size so we can see the effect of the
  426.             useWFont variation code. We need to do this once every time through
  427.             the event loop since (in system 6.0) DialogSelect resets the font
  428.             to the system font. */
  429.         SetPort(dlg);
  430.         TextFont(geneva);
  431.         TextSize(9);
  432.     
  433.         /* handle the next event; this is a pretty simple (but inelegant)
  434.             event loop */
  435.         SetCursor(&arrow);
  436.         (void) EventGet(everyEvent, &event, GetCaretTime(), NULL);
  437.         switch (event.what) {
  438.         case updateEvt:
  439.             /* handle an update event */
  440.             window = (WindowPtr) event.message;
  441.             BeginUpdate(window);
  442.             if (window == dlg) {
  443.                 DrawDialog(dlg);
  444.                 event.what = nullEvent;
  445.             }
  446.             EndUpdate(window);
  447.             break;
  448.         case mouseDown:
  449.             /* handle a click in a window's drag bar */
  450.             switch (FindWindow(event.where, &window)) {
  451.             case inDrag:
  452.                 DragWindow(window, event.where, &screenBits.bounds);
  453.                 break;
  454.             }
  455.             break;
  456.         }
  457.         
  458.         /* handle dialog events */
  459.         if (IsDialogEvent(&event)) {
  460.             DialogPtr dlgHit;
  461.             short itemHit;
  462.             Point where;
  463.             
  464.             /* TYPEIN_BEGIN */
  465.             /* check if user is clicking in size type-in popup menu */
  466.             if (event.what == mouseDown) {
  467.                 where = event.where;
  468.                 GlobalToLocal(&where);
  469.                 if (FindDItem(dlg, where) + 1 == iSizePopup) {
  470.  
  471.                     /* adjust the size popup menu before it's pulled down */
  472.                     sizeHasCustomValue = AdjustTypeInPopupMenu(dlg,
  473.                                                     iSizePopup, iSizeText,
  474.                                                     sizeHasCustomValue);
  475.  
  476.                     /* outline font sizes available in the selected font */
  477.                     GetItem(GetCtlMenu(GetDControl(dlg, iFontPopup)),
  478.                         GetCtlValue(GetDControl(dlg, iFontPopup)), str);
  479.                     GetFNum(str, &font);
  480.                     OutlineFontSizes(GetCtlMenu(GetDControl(dlg, iSizePopup)),
  481.                         font, true);
  482.                 }
  483.             }
  484.             /* TYPEIN_END */
  485.             
  486.             /* handle the dialog event */
  487.             if (DialogSelect(&event, &dlgHit, &itemHit)) {
  488.             
  489.                 /* handle a click in one of the dialog's buttons */
  490.                 check(dlg == dlgHit);
  491.                 switch (itemHit) {
  492.                 case iQuit:
  493.                     quit = true;
  494.                     break;
  495.                 case iSizePopup:
  496.                     /* TYPEIN_BEGIN */
  497.                     /* Set the value displayed in the text field to the value
  498.                         chosen from the size popup menu and select the text in
  499.                         the size edit field. */
  500.                     GetItem(GetCtlMenu(GetDControl(dlg, iSizePopup)),
  501.                         GetCtlValue(GetDControl(dlg, iSizePopup)), str);
  502.                     SetDText(dlg, iSizeText, str);
  503.                     SelIText(dlg, iSizeText, 0, 32767);
  504.                     break;
  505.                     /* TYPEIN_END */
  506.                 }
  507.             }
  508.         }
  509.     }
  510.     
  511.     /* dispose of everything */
  512.     #if CDEF_ATTACH
  513.         for (i = 0; popups[i] != iLast; i++)
  514.             PopupCDEFDetach(GetDControl(dlg, popups[i]));
  515.     #endif /* CDEF_ATTACH */
  516.     DisposeDialog(dlg);
  517. }
  518.  
  519. void main(void)
  520. {
  521.     HeapInit(0, 4);
  522.     ManagersInit();
  523.     Run();
  524. }
  525.